/*
 * ﻿Copyright (C) 2012-2013 NewMain Softech
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package com.newmainsoftech.ant.types.maven;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.maven.repository.internal.MavenRepositorySystemSession;
import org.apache.maven.repository.internal.MavenServiceLocator;
import org.apache.maven.wagon.Wagon;
import org.apache.maven.wagon.providers.http.HttpWagon;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.LogLevel;
import org.apache.tools.ant.types.Path;
import org.sonatype.aether.RepositorySystem;
import org.sonatype.aether.collection.CollectRequest;
import org.sonatype.aether.connector.wagon.WagonProvider;
import org.sonatype.aether.connector.wagon.WagonRepositoryConnectorFactory;
import org.sonatype.aether.graph.Dependency;
import org.sonatype.aether.graph.DependencyNode;
import org.sonatype.aether.repository.LocalRepository;
import org.sonatype.aether.repository.RemoteRepository;
import org.sonatype.aether.resolution.DependencyRequest;
import org.sonatype.aether.spi.connector.RepositoryConnectorFactory;
import org.sonatype.aether.util.artifact.DefaultArtifact;
import org.sonatype.aether.util.graph.PreorderNodeListGenerator;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import com.newmainsoftech.ant.MavenProjectAccessor;
import com.newmainsoftech.ant.taskdefs.maven.MavenProperty;

/**
 * Extension of {@link Path} Ant type for Maven dependency. <br />
 * Though Maven is known by its dependency management, occasionally may encounter the situation that  
 * it is necessary <b>to calculate path to the .jar file, what is not defined in the Maven project's 
 * pom.xml file, in Maven local repository</b>. This class and maven-antrun-plugin can help such 
 * situation. <br />
 * This can be used in Ant stand along environment too to take advantage of Maven repository system. 
 * <ul>
 * <li><h3><a id="usage">Usage examples</a></h3>
 * Let's say that you like to have the path to gwt-dev.jar file in the Maven local repository. <br />
 * Since gwt-dev.jar contains several popular framework classes, having it defined as a dependency in 
 * pom.xml can cause class loading problem if your project use those classes, but you may be in the 
 * situation requiring it to compile your classes. 
 * <ul>
 * <li>Define <code>mvnpath</code> tag in Ant's <code>target</code> tag (what can be also 
 * defined in <code>configuration</code> tag of manve-antrun-plugin's <code>execution</code> tag 
 * as well as in an Ant file) like:<br />
 * <blockquote><code>
 * &lt;<b>mvnpath</b> id=&quot;gwtdev_path&quot;&gt;<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;<b>mvnpathelement <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dependencyid</b>=&quot;com.google.gwt:gwt-dev:2.4.0&quot; /&gt;<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;...<br />
 * &lt;/mvnpath&gt;<br />
 * </code></blockquote>
 * <b>By referring this "gwtdev_path" <code>mvnpath</code> tag as you normally do with regular Ant's 
 * <code>path</code> tag</b> in your Ant file, you <b>can have path to the gwt-dev.jar file (and paths 
 * to the sub-dependencies' ones)</b> in the Maven local repository. If the gwt-dev.jar file does <b>not 
 * exist in the Maven local repository</b>, it will be <b>automatically downloaded from the remote 
 * repository</b>.<br />
 * In above definition, you <b>can use property for providing <code>dependencyid</code> attribute value</b>. 
 * For an example, the version number part (or any other parts) of dependency ID could be provided 
 * via property like:
 * <blockquote><code>com.google.gwt:gwt-dev:<b>${gwt.version}</b></code></blockquote> 
 * </li>
 * <li><b>To specify remote repository</b> other than Maven central, <b>use <code>remoterepo</code> 
 * attribute of <code>mvnpathelement</code> tag</b><br />
 * <blockquote><code>
 * &lt;mvnpath id=&quot;mycila_jdk-logging-perwebapp_path&quot;&gt;<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;mvnpathelement<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>remoterepo</b>=&quot;mc-release,default,http://mc-repo.googlecode.com/svn/maven2/releases&quot;<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dependencyid=&quot;com.mycila:jdk-logging-perwebapp:1.2.ga&quot; /&gt;<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;/mvnpath&gt;<br />
 * </code></blockquote>
 * Using property for providing <code>remoterepo</code> attribute value is supported.<br />
 * <br />
 * </li>
 * <li><b>To specify local repository</b> other than default one, <b>use <code>localrepo</code> attribute 
 * of <code>mvnpath</code> tag</b>.<br />
 * <blockquote><code>
 * &lt;mvnpath id=&quot;some_dependency_path&quot; <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;<b>localrepo</b>=&quot;${temp_local_repo_path}&quot;&gt;<br />
 * &nbsp;&nbsp;&nbsp;&nbsp;...<br />
 * &lt;/mvnpath&gt;<br />
 * </code></blockquote>
 * Using property for providing <code>localrepo</code> attribute value is supported as the above example.<br />
 * <br />
 * </li>
 * <li>If you need <b>to have just string of gwt-dev.jar file path</b> (and do not care of sub-dependency), 
 * then you can <b>use {@link MavenProperty}</b> like: <br />
 * <blockquote><code>
 * &lt;<b>mvnproperty <br />
 * &nbsp;&nbsp;&nbsp;&nbsp;dependency</b>=&quot;com.google.gwt:gwt-dev:2.4.0&quot; /&gt;
 * </code></blockquote>
 * After that, the path to the gwt-dev.jar file can be obtained as the value of 
 * ${com.google.gwt:gwt-dev:jar} property like the property for the dependency regularly defined 
 * in the project's pom.xml file. <br />
 * <br />
 * </li>
 * <li>Programmatic way<br />
 * In Ant's script tag:<br />
 * <blockquote><code>
 * MavenPath mavenPath = new MavenPath( project);<br />
 * {@link MvnPathElement} mvnPathElement = mavenPath.createMvnPathElement();<br />
 * mvnPathElement.setDependencyId( "com.google.gwt:gwt-dev:2.4.0");<br />
 * </code><br />
 * See also: 
 * <ul>
 * <li>{@link MavenPath#createMvnPathElement()}</li>
 * <li>{@link MvnPathElement#setDependencyId(String)}</li>
 * <li>{@link MvnPathElement#setRemoteRepo(String)}</li>
 * <li>{@link #setLocalRepo(String)}</li>
 * </ul>
 * </blockquote>
 * After those, use:
 * <ul>
 * <li>{@link MvnPathElement#getDependencyPath}</li>
 * <li>{@link MvnPathElement#getDependencyPathsPack()}</li>
 * <li>{@link MvnPathElement#getDependencyId()}</li>
 * <li>{@link MvnPathElement#getParts()}</li>
 * <li>{@link #list()}</li>
 * </ul>
 * </li>
 * </ul>
 * </li>
 * <li><h3><a id="configuration">Configuration</a></h3>
 * <ul>
 *  <li>In the <b>case of using maven-antrun-plugin</b><br />
 *  	<ol>
 *  	 <li>Define com.newmainsoftech:ant-util:jar as a dependency of maven-antrun-plugin:
 *  		<blockquote><code>
 *  		&lt;<b>plugin</b>&gt;<br />
 *  		&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;<br />
 *  		&nbsp;&nbsp;&nbsp;&nbsp;&lt;<b>artifactId</b>&gt;<b>maven-antrun-plugin</b>&lt:/artifactId&gt;<br />
 *  		&nbsp;&nbsp;&nbsp;&nbsp;...<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;dependencies&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;<b>dependency</b>&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;<b>&lt;groupId&gt;com.newmainsoftech&lt;/groupId&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;ant-util&lt;/artifactId&gt;</b><br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;#.#.#&lt;/version&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;/dependency&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;...<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;/dependencies&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;...<br />
 *  		&lt;/plugin&gt;<br />
 *  		</code></blockquote>
 * 		 </li>
 * 		 <li>Define <code>taskdef</code> tag under execution's configuration tag like:<br />
 * 			<blockquote><code>
 *  		&lt;<b>plugin</b>&gt;<br />
 *  		&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;<br />
 *  		&nbsp;&nbsp;&nbsp;&nbsp;&lt;<b>artifactId</b>&gt;<b>maven-antrun-plugin</b>&lt:/artifactId&gt;<br />
 *  		&nbsp;&nbsp;&nbsp;&nbsp;...<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;executions&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;<b>execution</b>&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;id&gt;as_you_like&lt;/id&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;phase&gt;as you need&lt;/phase&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;goals&gt;&lt;goal&gt;run&lt;/goal&gt;&lt;/goals&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;<b>configuration</b>&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;target name="what_ever_you_like"&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;<b>taskdef <br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resource="net/sf/antcontrib/antlib.xml" <br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classpathref="maven.plugin.classpath"</b> /&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;!-- Now you can use mvnpath or mvnproperty tag --&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;...<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;mvnproperty dependency="com.google.gwt:gwt-dev:2.4.0" /&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;/target&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;/configuration&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;...<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;/execution&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;
 * 			&nbsp;&nbsp;&nbsp;&nbsp;...<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;&lt;/executions&gt;<br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;...<br />
 *  		&lt;/plugin&gt;<br />
 * 			</code></blockquote>
 * 		 </li>
 * 		</ol>
 *  </li>
 *  <li>If you cannot define com.newmainsoftech:ant-util:jar as a dependency in your project's
 *  	pom.xml, or the case of stand alone Ant environment
 *  	<ol>
 *  	 <li>Define <code>path</code> tag for this project's archive ant-util.jar file and 
 *  		sub-dependencies.<br />
 *  		You need to provide each path of the following components:
 * 			<ul>
 * 			 <li>com.newmainsoftech:ant-util:jar</li>
 * 			 <li>org.sonatype.aether:aether-api:jar</li>
 * 			 <li>org.sonatype.aether:aether-util:jar</li>
 * 			 <li>org.sonatype.aether:aether-spi:jar</li>
 * 			 <li>org.sonatype.aether:aether-connector-wagon:jar</li>
 * 			 <li>org.apache.httpcomponents:httpcore:jar</li>
 * 			 <li>commons-logging:commons-logging:jar</li>
 * 			 <li>org.apache.httpcomponents:httpclient:jar</li>
 * 			 <li>org.apache.maven.wagon:wagon-http:jar</li>
 * 			 <li>org.apache.maven.wagon:wagon-http-shared4:jar</li>
 * 			</ul>
 * 			<br />
 * 			Define a <code>path</code> tag with those paths as the alternative of 
 * 			<code>maven.plugin.classpath</code> path reference of the above maven-antrun-plugin example:
 * 				<blockquote><code>
 * 				&lt;<b>path</b> <b>id</b>=&quot;<b>antutil_path</b>&quot; <br />
 * 				&nbsp;&nbsp;&nbsp;&nbsp;path=&quot;${lengthy_path_of_antutil_and_fellows}&quot; /&gt;
 * 				</code></blockquote>
 *  	 </li>
 *  	 <li>Define <code>taskdef</code> tag for the ant-util's antlib.xml<br />
 * 			<blockquote><code>
 * 			&lt;<b>taskdef</b> <br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;<b>resource=&quot;com/newmainsoftech/ant/antlib.xml&quot;</b> <br />
 * 			&nbsp;&nbsp;&nbsp;&nbsp;<b>classpathref</b>=&quot;<b>antutil_path</b>&quot; /&gt;
 * 			</code></blockquote>
 *  	 </li>
 *  	</ol>
 *  </li>
 * </ul>
 * </li>
 * </ul>
 * @author <a href="mailto:artymt@gmail.com">Arata Y.</a>
 * @see MvnPathElement
 * @see Path
 * @see MavenProperty
 */
public class MavenPath extends Path {
	// Regarding localRepository attribute --------------------------------------------------------
	/**
	 * Replace property expression to real value in <code>targetToken</code> input. 
	 * 
	 * @param targetToken
	 * @return String with property values replaced from property expressions
	 */
	protected String parseProperty( final String targetToken) {
		if ( targetToken == null) return null;
		
		String parsedResult = targetToken.trim();
		for( int openIndex = parsedResult.indexOf( "${"); 
				openIndex > -1; 
				openIndex = parsedResult.indexOf( "${")) 
		{
			int closeIndex = parsedResult.indexOf( "}", openIndex + 2);
			if ( closeIndex < 0) break; // for
			String propertyName = parsedResult.substring( openIndex + 2, closeIndex);
			
			MavenProjectAccessor mavenProjectAccessor = new MavenProjectAccessor();
			String propertyValue = mavenProjectAccessor.getMavenProperty( this, propertyName);
				if ( propertyValue == null) {
					Project antProject = getProject();
					if ( antProject != null) {
						propertyValue = antProject.getProperty( propertyName);
					}
				}
				if ( propertyValue == null) {
					propertyValue = System.getProperty( propertyName.trim());
				}
			if ( propertyValue != null) {
				parsedResult 
				= parsedResult.replace( 
						"${" + propertyName + "}", propertyValue);
			}
		} // for
		return parsedResult;
	}
	
	// For obtaining Maven installation directory -------------------------------------------------
	protected String readM2SystemEnv( final Project antProject) {
		String m2Path = System.getenv( "M2_HOME");
			if ( m2Path == null) {
				m2Path = System.getenv( "M2");
			}
			if ( m2Path != null) {
				File m2DirFile = new File( m2Path);
				if ( "bin".equals( m2DirFile.getName().toLowerCase())) {
					m2DirFile = m2DirFile.getParentFile();
					m2Path = m2DirFile.getPath();
				}
			}
			if (( m2Path == null) && (antProject != null)) {
				antProject.log( 
						"Neither of \"M2_HOME\" nor \"M2\" system environment variable " 
						+ " has not been defined.", 
						LogLevel.WARN.getLevel());
			}
		return m2Path;
	}
	public static final String MavenHomePropertyName = "maven.home";
		/**
		 * @return {@value #MavenHomePropertyName}
		 */
		public static String getMavenHomePropertyName() {
			return MavenHomePropertyName;
		}
	protected boolean checkM2InstallationDir( final String m2Path) {
		String m2PathCopy = ((m2Path == null) ? "" : m2Path.trim());
			if ( m2PathCopy.length() < 1) return false;
		File m2DirFile = new File( m2PathCopy);
		if ( !m2DirFile.exists() || !m2DirFile.isDirectory()) {
			return false;
		}
		return true;
	}
	/**
	 * Locate Maven installation directory by reading system environment. 
	 * @return Maven installation directory or null when failed to locate it.
	 */
	public File getM2InstallationDir() {
		String m2Path = null;
			final Project antProject = getProject();
				if ( antProject != null) {
					MavenProjectAccessor mavenProjectAccessor = new MavenProjectAccessor();
					m2Path 
					= mavenProjectAccessor.getMavenProperty( 
							antProject, MavenPath.getMavenHomePropertyName());
					if ( m2Path != null) {
						log(
								String.format(
										"Via %1$s Maven property, obtained Maven installation " 
										+ "directory: %2$s.",
										MavenPath.getMavenHomePropertyName(), 
										m2Path
										), 
								LogLevel.DEBUG.getLevel()
								);
					}
					else {
						m2Path = antProject.getProperty( MavenPath.getMavenHomePropertyName());
						if ( m2Path != null) {
							log( 
									String.format(
											"Via %1$s Ant property, obtained Maven installation " 
											+ "directory: %2$s.",
											MavenPath.getMavenHomePropertyName(), 
											m2Path
											), 
									LogLevel.DEBUG.getLevel()
									);
						}
					}
				}
			if ( m2Path == null) { // Can be Ant stand alone use case.
				m2Path = readM2SystemEnv( antProject);
			}
			
		if ( checkM2InstallationDir( m2Path)) {
			File m2DirFile = new File( m2Path);
			return m2DirFile;
		}
			if ( m2Path != null) {
				log(
						String.format(
								"%1$s is not valid as Maven installation directory.",
								m2Path
								),
						LogLevel.WARN.getLevel());
			}
		return null;
	}
	
	/**
	 * @deprecated Use either <code>{@link #getM2InstallationDir}</code> or 
	 * <code>{@link #readM2SystemEnv(Project)}</code> method instead.
	 * @return
	 */
	protected String readDefaultMavenHome() {
		File m2DirFile = getM2InstallationDir();
			if ( m2DirFile == null) return null;
		
		File m2ConfFile = new File( m2DirFile, "bin".concat(  File.separator).concat( "m2.conf"));
			if ( !m2ConfFile.exists()) {
				log( 
						String.format( 
								"Anxious incident: %1$s not found.", 
								m2ConfFile.getPath()));
				return null;
			}
		
		String userM2HomePath = null;
			Exception cause = null;
			try {
				String targetStringInM2ConfFile = "set maven.home default";
				FileReader fileReader = new FileReader( m2ConfFile);
				BufferedReader bufferedReader = new BufferedReader( fileReader);
				for ( 
						String redLine = bufferedReader.readLine();
						redLine != null;
						redLine = bufferedReader.readLine()
						) 
				{
					int targetStrIndex = redLine.indexOf( targetStringInM2ConfFile);
					if ( targetStrIndex < 0) continue; //for
					userM2HomePath 
					= redLine.substring(
							targetStrIndex + targetStringInM2ConfFile.length())
							.trim();
					break; // for
				} // for
				bufferedReader.close();
			}
			catch( Exception exception) {
				cause = exception;
			}
			
			if ( userM2HomePath == null) {
				if ( cause == null) {
					log( 
							String.format(
									"Could not read default setting for maven.home in %1$s.", 
									m2ConfFile.getPath())
							);
				}
				else {
					log( 
							String.format(
									"Could not read default setting for maven.home in %1$s.%n" 
									+ "Exception caught: %2$s", 
									m2ConfFile.getPath(), cause.toString())
							);
				}
				return null;
			}
			
		userM2HomePath = parseProperty( userM2HomePath);
			log(
					String.format(
							"Having read maven home directory path (%1$s) from %2$s.",
							userM2HomePath, 
							m2ConfFile.getAbsolutePath()
							),
					LogLevel.VERBOSE.getLevel());
		return userM2HomePath;
	}
	// --------------------------------------------------------------------------------------------
	
	protected File getUserM2SettingXml() {
		File settingsXmlFile
		= new File( 
				System.getProperty( "user.home") + File.separator 
				+ ".m2" + File.separator + "settings.xml");
			if ( !settingsXmlFile.exists()) {
				log(
						String.format(
								"%1$s does not exist.",
								settingsXmlFile.getPath()
								), 
						LogLevel.INFO.getLevel());
				return null;
			}
			else if ( !settingsXmlFile.isFile()) {
				log(
						String.format(
								"%1$s is not file.",
								settingsXmlFile.getPath()
								), 
						LogLevel.WARN.getLevel());
				return null;
			}
		return settingsXmlFile;
	}
	
	protected File getGlobalM2SettingXml() {
		File m2DirFile = getM2InstallationDir();
			if ( m2DirFile == null) return null;
			
		File settingsXmlFile = new File( m2DirFile, "conf" + File.separator + "settings.xml");
			if ( !settingsXmlFile.exists()) {
				log(
						String.format(
								"%1$s does not exist.",
								settingsXmlFile.getPath()
								), 
						LogLevel.WARN.getLevel());
				return null;
			}
			else if ( !settingsXmlFile.isFile()) {
				log(
						String.format(
								"%1$s is not file.",
								settingsXmlFile.getPath()
								), 
						LogLevel.WARN.getLevel());
				return null;
			}
		return settingsXmlFile;
	}
	
	/**
	 * Get String data pointed by <code>xPathExpression</code> input from Maven's settings.xml.
	 * <ol> 
	 * <li>It tries to read from settings.xml in user home directory.</li>
	 * <li>If it fails, then it tries to read from one in conf sub-directory of 
	 * Maven installation directory.</li>
	 * </ol>
	 * 
	 * @param xPathExpression must point to (String) data in settings.xml.
	 * @return String data out of Maven's settings.xml, or null when failed to locate it.
	 * @throws FactoryConfigurationError, ParserConfigurationException, IOException, SAXException, 
	 * XPathExpressionException, and RuntimeException kinds
	 */
	protected String parseM2SettingXml( final String xPathExpression) 
	throws FactoryConfigurationError, ParserConfigurationException, 
			IOException, SAXException, XPathExpressionException {
		String xPathExpressionCopy = ((xPathExpression == null) ? "" : xPathExpression.trim());
			if ( xPathExpressionCopy.length() < 1) {
				throw new IllegalArgumentException(
						String.format(
								"\"%1$s\" is not valid XPath string.",
								xPathExpression)
						);
			}
			
		// Consult Maven's setting.xml ------------------------------------------------------------
		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
		XPath xPathObj = XPathFactory.newInstance().newXPath();
		
		// Try settings.xml in user home directory first
		File settingsXmlFile = getUserM2SettingXml();
			if ( settingsXmlFile != null) {
				Document document = documentBuilder.parse( settingsXmlFile);
					document.getDocumentElement().normalize();
					Element documentElement = document.getDocumentElement();
						// Read somewhere about necessity of obtaining document element after normalize
				
				String parsedStr = xPathObj.evaluate( xPathExpressionCopy, document).trim();
				if ( parsedStr.length() > 0) {
					log(
							String.format(
									"Find \"%1$s\" at \"%2$s\" XPath expression in %3$s.",
									parsedStr, 
									xPathExpressionCopy,
									settingsXmlFile.getAbsolutePath()
									), 
							LogLevel.VERBOSE.getLevel());
					return parsedStr;
				}
				else {
					log(
							String.format(
									"Could not locate an element pointed by \"%1$s\" XPath expression in %2$s.",
									xPathExpressionCopy,
									settingsXmlFile.getAbsolutePath()
									), 
							LogLevel.VERBOSE.getLevel());
				}
//TODO need to test whether null is returned when element in settings.xml cannot be located by XPath
			}
			
		documentBuilder.reset();
		xPathObj.reset();
		
		// Fall back to settings.xml in Maven's installation directory
		settingsXmlFile = getGlobalM2SettingXml();
			if ( settingsXmlFile != null) {
				Document document = documentBuilder.parse( settingsXmlFile);
					document.getDocumentElement().normalize();
					Element documentElement = document.getDocumentElement();
						// Read somewhere about necessity of obtaining document element after normalize
				
				String parsedStr = xPathObj.evaluate( xPathExpressionCopy, document).trim();
				if ( parsedStr.length() > 0) {
					log(
							String.format(
									"Find \"%1$s\" at \"%2$s\" XPath expression in %3$s.",
									parsedStr, 
									xPathExpressionCopy,
									settingsXmlFile.getAbsolutePath()
									), 
							LogLevel.VERBOSE.getLevel());
					return parsedStr;
				}
				else {
					log(
							String.format(
									"Could not locate an element pointed by \"%1$s\" XPath expression in %2$s.",
									xPathExpressionCopy,
									settingsXmlFile.getAbsolutePath()
									), 
							LogLevel.VERBOSE.getLevel());
				}
			}
		// ----------------------------------------------------------------------------------------
			
		log(
				String.format(
						"Returning null since Maven's user and global settings.xml does not have content " 
						+ "located by \"%1$s\" XPath expression.",
						xPathExpressionCopy
						), 
				LogLevel.VERBOSE.getLevel());
		return null;
	}
	
	private final static String DefaultLocalRepositoryPath 
	= System.getProperty( "user.home") + File.separator + ".m2" + File.separator + "repository";
		public static String getDefaultLocalRepositoryPath() {
			return DefaultLocalRepositoryPath;
		}

	private static final String LocalRepositoryPropertyName = "settings.localRepository";
		public static String getLocalRepositoryPropertyName() {
			return LocalRepositoryPropertyName;
		}
	private static final String LocalRepositoryElementXPathExpression = "/settings/localRepository[1]";
		public static String getLocalRepositoryElementXPathExpression() {
			return LocalRepositoryElementXPathExpression;
		}
		
	/**
	 * Resolve path to Maven local repository root directory.
	 * It will make attempt by the following order:
	 * <ol>
	 * <li>get value of settings.localRepository property.</li>
	 * <li>read from Maven's user settings.xml.</li>
	 * <li>read from Maven's global settings.xml.</li>
	 * <li>return default Maven local repository path 
	 * (obtained via {@link #getDefaultLocalRepositoryPath} method).</li>
	 * </ol>
	 * @return path to Maven local repository root directory 
	 */
	protected String resolveLocalRepositoryPath() {
		String localRepositoryStr 
		= getProject().getProperty( MavenPath.getLocalRepositoryPropertyName());
			if ( localRepositoryStr != null) {
			// It was maven-antrun-plugin environment
				log(
						String.format(
								"Obtained local repository location (%1$s) from %2$s property.",
								localRepositoryStr, MavenPath.getLocalRepositoryPropertyName()
								), 
						LogLevel.VERBOSE.getLevel());
				return localRepositoryStr;
			}
			
		// May be Ant stand-alone environment
		// Consult Maven's setting.xml for local repository path ------------------------------
		Throwable cause = null;
		try {
			localRepositoryStr 
			= parseM2SettingXml( MavenPath.getLocalRepositoryElementXPathExpression());
		}
		catch( Throwable throwable) {
			cause = throwable;
		}
		localRepositoryStr = ((localRepositoryStr == null) ? "" : localRepositoryStr);
			if ( localRepositoryStr.length() > 0) {
//TODO what does Maven mean for '~' character of "~/.m2/repository" in settings.xml?
				if ( localRepositoryStr.startsWith( "~/")) {
					localRepositoryStr 
					= System.getProperty( "user.home") + File.separator 
						+ localRepositoryStr.substring( 2);
				}
				if ( localRepositoryStr.contains( "/")) {
					if ( !localRepositoryStr.toLowerCase().startsWith( "file:")) {
						localRepositoryStr = localRepositoryStr.replace( "/", File.separator);
					}
				}
				log(
						String.format(
								"Maven local repository location (%1$s) was obtained from " 
								+ "Maven's settings.xml.",
								localRepositoryStr
								), 
						LogLevel.VERBOSE.getLevel());
			}
			else {
				localRepositoryStr = MavenPath.getDefaultLocalRepositoryPath();
				if ( cause == null) {
					log(
							String.format(
									"Fall back to use default local repository location (%1$s) since " 
									+ "Maven's settings.xml does not contain local repository setting.",
									localRepositoryStr
									), 
							LogLevel.INFO.getLevel());
				}
				else {
					log(
							String.format(
									"Fall back to use default local repository location (%1$s) since " 
									+ "failed to read local repository setting from Maven's settings.xml " 
									+ "due to exception: %2$s",
									localRepositoryStr, cause.toString()
									), 
							LogLevel.WARN.getLevel());
				}
			}
		return localRepositoryStr;
		// ------------------------------------------------------------------------------------
	}
	
	String localRepo;
		public synchronized String getLocalRepo() {
			if ( localRepo == null) {
				setLocalRepo( resolveLocalRepositoryPath());
			}
			return localRepo;
		}
		/**
		 * To specify path to root directory of Maven local repository other than default one.
		 * 
		 * @param localRepo path to root directory of Maven local repository. <br />
		 * Using property to specify value is also supported: ex. "${locaRepo}".
		 */
		public synchronized void setLocalRepo( final String localRepo) {
			this.localRepo = parseProperty( localRepo);
		}
	// --------------------------------------------------------------------------------------------

	// Aether configuration -----------------------------------------------------------------------
	public static class ManualWagonProvider implements WagonProvider {
		@Override
		public Wagon lookup( String roleHint) throws Exception {
	        if ( ( roleHint != null) && roleHint.toLowerCase().startsWith( "http") ) {
//	            return new LightweightHttpWagon();
	            return new HttpWagon();
	        }
	        return null;
		}

		@Override
		public void release( Wagon wagon) { // No operation
//TODO do I need to release wagon? If so, how to release wagon?
		}
	}
	
	private static MavenServiceLocator mavenServiceLocator = new MavenServiceLocator();
		static {
			mavenServiceLocator.setService( WagonProvider.class, ManualWagonProvider.class);
			mavenServiceLocator.addService(
					RepositoryConnectorFactory.class, WagonRepositoryConnectorFactory.class);
		};
		protected static MavenServiceLocator getMavenServiceLocator() {
			return mavenServiceLocator;
		}
	private static RepositorySystem repositorySystem 
	= getMavenServiceLocator().getService( RepositorySystem.class );
		protected static RepositorySystem getRepositorySystem() {
			return repositorySystem;
		}
	// --------------------------------------------------------------------------------------------

	/**
	 * Extension of {@link PathElement} class to resolve path to Maven dependency in local repository.
	 */
	public class MvnPathElement extends PathElement {
		private Pattern pattern 
		= Pattern.compile( "^([^\\$\\{:\\}]+):([^\\$\\{:\\}]+):([^\\$\\{:\\}]+)");
			protected Pattern getPattern() {
				return pattern;
			}
			protected void setPattern( Pattern pattern) {
				this.pattern = pattern;
			}
		/**
		 * Only when current running scope of Maven project satisfies one specified by this 
		 * member field (what is delegate of attribute of corresponding XML element), path to 
		 * dependency will be set.
		 */
		private String scope = "compile";
			protected String getScope() {
				return scope;
			}
			public void setScope( String scope) {
				String scopeCopy = ((scope == null) ? "" : scope.toLowerCase().trim());
					if ( scopeCopy.length() < 1) return;
				this.scope = parseProperty( scopeCopy);
			}
		
		/**
		 * Get string (path) value for dependency when it's found that either a property or a reference 
		 * of Ant project has already been defined for it.
		 * 
		 * @param artifactJarMavenDialect specifies dependency to check for in 
		 * groupID:artifactID:type format.
		 * @return string (path) value for specified dependency or null.
		 */
		protected String getDeclaredDependency( final String artifactJarMavenDialect) {
			Project antProject = getProject();
			if ( antProject != null) {
				// Regarding case of maven-antrun-plugin 
				String dependencyJarPath = antProject.getProperty( artifactJarMavenDialect);
					if ( dependencyJarPath != null) {
						return dependencyJarPath;
					}
				Object dependencyJarObj = antProject.getReference( artifactJarMavenDialect);
				if ( dependencyJarObj != null) {
					dependencyJarPath = dependencyJarObj.toString();
					return dependencyJarPath;
				}
			}
			return null;
		}
		
		// Regarding remote repository attribute --------------------------------------------------
		public class RemoteRepoId {
			private String id;
				String getId() {
					return id;
				}
				void setId(String id) {
					this.id = id;
				}
			private String type;
				String getType() {
					return type;
				}
				void setType(String type) {
					this.type = type;
				}
			private String url;
				String getUrl() {
					return url;
				}
				void setUrl(String url) {
					this.url = url;
				}
			public RemoteRepoId() {
			}
			public RemoteRepoId( String id, String type, String url) {
				this.id = id;
				this.type = type;
				this.url = url;
			}
		}
		protected final RemoteRepoId defaultRemoteRepoId 
		= new RemoteRepoId( "central", "default", "http://repo1.maven.org/maven2/");
			protected RemoteRepoId getDefaultRemoteRepoId() {
				return defaultRemoteRepoId;
			}
			
		String remoteRepo 
		= defaultRemoteRepoId.getId() 
			+ "," + defaultRemoteRepoId.getType() + "," 
			+ defaultRemoteRepoId.getUrl();
		
			protected String getRemoteRepo() {
				return remoteRepo;
			}
			protected RemoteRepoId parseRemoteRepo() {
				RemoteRepoId remoteRepoId = new RemoteRepoId();
					String remoteRepoStr = getRemoteRepo();
					int index = 0;
					for( String remoteRepoToken : remoteRepoStr.split( ",")) {
						switch( index) {
						case 0:
							remoteRepoId.setId( 
									(remoteRepoToken.trim().length() < 1) ? null : remoteRepoToken.trim()
									);
							break; // switch
						case 1:
							remoteRepoId.setType( 
									(remoteRepoToken.trim().length() < 1) ? null : remoteRepoToken.trim()
									);
							break; // switch
						case 2:
							remoteRepoId.setUrl( 
									(remoteRepoToken.trim().length() < 1) ? null : remoteRepoToken.trim()
									);
							break; // switch
						} // switch;
						if ( ++index > 2) break; // for
					} // for
				if ( remoteRepoId.getUrl() == null) {
					return getDefaultRemoteRepoId();
				}
				return remoteRepoId;
			}
			/**
			 * To set remote repository being used to download dependency other than Maven central 
			 * repository.  
			 * @param remoteRepo String of what the value must be in the format of "id,type,URL". <br />
			 * Default is "central,default,http://repo1.maven.org/maven2/". <br />
			 * Using property to specify value is also supported: ex. "${repoId},default,${repoURL}".
			 */
			public void setRemoteRepo( String remoteRepo) {
				this.remoteRepo = parseProperty( remoteRepo);
			}
		// ----------------------------------------------------------------------------------------
		private Dependency dependency;
			protected Dependency getDependency() {
				return dependency;
			}
			private void setDependency( final Dependency dependency) {
				this.dependency = dependency;
			}
		private PreorderNodeListGenerator preorderNodeListGenerator;
			protected PreorderNodeListGenerator getPreorderNodeListGenerator() {
				return preorderNodeListGenerator;
			}
			private void setPreorderNodeListGenerator( 
					final PreorderNodeListGenerator preorderNodeListGenerator) 
			{
				this.preorderNodeListGenerator = preorderNodeListGenerator;
			}
		private String dependencyPathsPack;
			/**
			 * Return paths (delimited by a ';' character) resolved from Maven's dependency format string 
			 * given to {@link #setDependency} method. 
			 * The .jar file corresponding to <code>dependencyPhrase</code> input of 
			 * <code>setDependency</code> method should comes first when that .jar file requires 
			 * sub-dependencies. 
			 * @return paths delimited by a ';' character.
			 */
			public String getDependencyPathsPack() {
				return dependencyPathsPack;
			}
			private void setDependencyPathsPack( final String dependencyPathsPack) {
				this.dependencyPathsPack = dependencyPathsPack;
			}
		private String dependencyPath;
			public String getDependencyPath() {
				return dependencyPath;
			}
			private void setDependencyPath( final String dependencyPath) {
				this.dependencyPath = dependencyPath;
			}
		private String dependencyId;
			public String getDependencyId() {
				return dependencyId;
			}
			/**
			 * Resolve Maven's dependency format string to actual path to .jar file 
			 * in Maven's local repository, and set that path.
			 * <ol>
			 * <li>If property has been defined for the value of <code>dependencyPhrase</code> input, 
			 * then set that value without acquiring to repository.</li>
			 * <li>If the dependency .jar file is not found in Maven's local repository:
			 * <ul>
			 * <li>the dependency .jar file (including .jar files of sub-dependency) will be downloaded 
			 * from remote repository.</li>
			 * <li>the path to .jar file (including .jar files of sub-dependency) in local repository will 
			 * be set.</li>
			 * </ul>
			 * </li> 
			 * </ol> 
			 * 
			 * @param dependencyId specifies dependency by Maven's groupID:artifactID:version format.
			 * @see #setRemoteRepo(String) 
			 */
			public void setDependencyId( final String dependencyId) {
				// Validate dependency input ------------------------------------------------------ 
				String dependencyIdCopy = ((dependencyId == null) ? "" : dependencyId.trim()); 
					if ( dependencyIdCopy.length() < 10) {
						throw new IllegalArgumentException(
								String.format(
										"Value of dependency input is not appropriate to indicate Maven " 
										+ "dependency (in groupId:artifactId:version syntax): %1$s",
										((dependencyId == null) ? "null" : dependencyId)
										)
								);
					}
				dependencyIdCopy = parseProperty( dependencyIdCopy);
					// if dependencyCopy contain unresolved property expression, then it should be 
					// whole token self in dependencyCopy.
					if ( dependencyIdCopy.contains( "${")) {
						if ( !dependencyIdCopy.startsWith( "${") || !dependencyIdCopy.endsWith( "}")) {
							throw new IllegalArgumentException(
									String.format(
											"Could not resolve property name(s) to value(s) in %1$s",
											dependencyId
											)
									);
						}
						int length = dependencyIdCopy.length();
						dependencyIdCopy = dependencyIdCopy.replace( "${", "").replace( "}", "");
						if ( (length + 3) != dependencyIdCopy.length()) {
							throw new IllegalArgumentException(
									String.format(
											"Could not resolve property name(s) to value(s) in %1$s",
											dependencyId
											)
									);
						}
					}
					
				String groupId = null;
				String artifactId = null;
				String version = null;
					Pattern patternObj = getPattern();
					Matcher matcher = patternObj.matcher( dependencyIdCopy);
					if ( !matcher.find()) {
						throw new IllegalArgumentException(
								String.format(
										"Value of dependency input is not appropriate to indicate Maven " 
										+ "dependency: %1$s",
										dependencyId
										)
								);
					}
					boolean isVersionSet = false;
					for( int groupIndex = 1; groupIndex <= matcher.groupCount(); groupIndex++) {
						switch( groupIndex) {
						case 1:
							groupId = matcher.group( groupIndex);
							break;
						case 2:
							artifactId = matcher.group( groupIndex);
							break;
						case 3:
							version = matcher.group( groupIndex);
							isVersionSet = true;
							break;
						} // switch
					} // for
					if ( !isVersionSet) {
						throw new IllegalArgumentException(
								String.format(
										"Value of dependency input is not appropriate to indicate " 
										+ "Maven dependency: %1$s. It should consist of \"groupID:" 
										+ "artifactID:version\".",
										dependencyId
										)
								);
					}
				// --------------------------------------------------------------------------------
				
				// Check whether dependency input points defined dependency in project pom.xml
				String artifactJarMavenDialect = groupId + ":" + artifactId + ":jar";
				String dependencyJarPath = getDeclaredDependency( artifactJarMavenDialect);
/* Decided to fetch dependency even it has already been defined as property or reference
					if ( dependencyJarPath != null) {
						this.dependencyId = groupId + ":" + artifactId + ":jar:" + version;
						setPath( dependencyJarPath);
						this.dependencyPath = dependencyJarPath;
						return;
					}
*/	
				// Create a Repository System Session
				RepositorySystem repositorySystemObj = MavenPath.getRepositorySystem();
				MavenRepositorySystemSession mavenRepositorySystemSession 
				= new MavenRepositorySystemSession();
					String localRepositoryStr = getLocalRepo();
					LocalRepository localRepository = new LocalRepository( localRepositoryStr);
					mavenRepositorySystemSession.setLocalRepositoryManager(
							repositorySystemObj.newLocalRepositoryManager( localRepository));
//TODO how to increase timeout period?
				CollectRequest collectRequest = new CollectRequest();
//TODO need to add feature to exclude some sub-dependencies.
//TODO add support to other type than jar
					Dependency dependencyObj 
					= new Dependency( 
							new DefaultArtifact( groupId + ":" + artifactId + ":" + version), 
							getScope());
					collectRequest.setRoot( dependencyObj);
					
					RemoteRepoId remoteRepoIdObj = parseRemoteRepo();
					RemoteRepository  remoteRepository 
					= new RemoteRepository( 
							remoteRepoIdObj.getId(), remoteRepoIdObj.getType(), remoteRepoIdObj.getUrl());
					collectRequest.addRepository( remoteRepository);
					
				PreorderNodeListGenerator preorderNodeListGeneratorObj = null;
				try {
					DependencyNode dependencyNode 
					= repositorySystemObj
						.collectDependencies( mavenRepositorySystemSession, collectRequest)
						.getRoot();
					DependencyRequest dependencyRequest = new DependencyRequest( dependencyNode, null);
					repositorySystemObj
						.resolveDependencies( mavenRepositorySystemSession, dependencyRequest);
						
					preorderNodeListGeneratorObj 
					= new PreorderNodeListGenerator();
						dependencyNode.accept( preorderNodeListGeneratorObj);
						
					dependencyPathsPack = preorderNodeListGeneratorObj.getClassPath();
				}
				catch( Exception exception) {
					if ( exception instanceof RuntimeException) throw (RuntimeException)exception;
					throw new RuntimeException(
							String.format(
									"Failure in resolving the local repository path for %1$s dependency " 
									+ "binary.",
									dependencyIdCopy
									),
							exception
							);
				}
				this.dependencyId = groupId + ":" + artifactId + ":jar:" + version;
				setDependency( dependencyObj);
				setPreorderNodeListGenerator( preorderNodeListGeneratorObj);
				setPath( dependencyPathsPack);
//				setDependencyPath( dependencyObj.getArtifact().getFile().getAbsolutePath());
					// above didn't work because dependencyObj.getArtifact().getFile() was null
				String[] partsArray = this.getParts();
				setDependencyPath( partsArray[ 0]);
					if ( partsArray.length > 1) {
						log(
								String.format(
										"Resolved %1$s dependency: %n%2$cpath: %3$s%" 
										+ "n%2$cpaths including sub-dependencies: %4$s",
										groupId + ":" + artifactId + ":" + "jar",
										'\t',
										this.dependencyPath, 
										this.dependencyPathsPack
										), 
								LogLevel.VERBOSE.getLevel());
					}
					else {
						log(
								String.format(
										"Resolved %1$s dependency: %n%2$cpath: %3$s", 
										groupId + ":" + artifactId + ":" + "jar",
										'\t',
										this.dependencyPath
										), 
								LogLevel.VERBOSE.getLevel());
					}
				
				if ( dependencyJarPath != null) {
					if ( !dependencyJarPath.equals( partsArray[ 0])) {
						log(
								String.format(
										"Property (or reference) has been defined for %1$s dependency, " 
										+ "but its value (%2$s) is not identical with one (%3$s) just " 
										+ "obtained.",
										groupId + ":" + artifactId + ":" + "jar",
										dependencyJarPath, 
										partsArray[ 0]
										),
								LogLevel.WARN.getLevel()
								);
					}
				}
			}
	}
	
	// Constructors ---------------------------------------------------------------------------
	public MavenPath( Project project) {
		super( project);
	}
	public MavenPath( Project project, String path) {
		super( project, path);
	}
	// ----------------------------------------------------------------------------------------
	
    public MavenPath createMvnPath() throws BuildException {
    	MavenPath mavenPath = new MavenPath( getProject());
        add( mavenPath);
        return mavenPath;
    }
	
	public MvnPathElement createMvnPathElement() throws BuildException {
        if (isReference()) {
            throw noChildrenAllowed();
        }
        MvnPathElement mvnPathElement = new MvnPathElement();
        add( mvnPathElement);
        return mvnPathElement;
	}
}